package com.xingyang.yutnori.ui;

import com.xingyang.yutnori.model.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Arrays;
import java.util.Collections;
import java.awt.Composite;
import java.awt.AlphaComposite;

/**
 * The main game board display component.
 * Handles rendering of the board, pieces, and user interactions.
 */
public class GameBoard extends JPanel {
    private Game game;
    private Board board;
    private List<Piece> selectedPieces;
    private Piece selectedPiece;  // Add missing field
    private boolean canSelectPieces;
    
    // Movement hint indicators
    private Map<Integer, Boolean> possibleMovePositions;
    private boolean showingHints = false;
    
    // 新增：多骰子结果选择功能
    private JPanel rollSelectionPanel;
    private boolean waitingForRollSelection = false;
    
    // 新增：缓存已记录的棋子位置，减少重复日志
    private Map<Integer, Integer> lastLoggedPositions = new HashMap<>();
    
    // UI constants
    private static final int BOARD_SIZE = 900; // Main board width
    private static final int BOARD_HEIGHT = 750; // Main board height
    private static final int GAME_AREA_SIZE = 580; // Game area within board
    private static final int PIECE_SIZE = 35;  // Piece size
    private static final int POSITION_SIZE = 40; // Position marker size
    
    // Piece offsets for stacked pieces
    private static final int[] STACK_OFFSETS_X = {0, 8, -8, 0};
    private static final int[] STACK_OFFSETS_Y = {0, 0, 0, 8};
    
    // Home areas for players' pieces not on board
    private Rectangle[] homeAreas;
    
    // Player movement hints
    private JLabel movementHintLabel;
    private JLabel currentActionLabel;  // New label for current required action
    
    /**
     * 添加脉冲动画效果的变量
     */
    private int pulseCounter = 0;
    private Timer pulseTimer;
    
    // Variables for animation
    private Point currentEffectPosition = null;
    private int currentEffectRadius = 0;
    private int maxEffectRadius = 0;
    private boolean effectActive = false;
    
    // 添加YutStickPanel引用
    private YutStickPanel yutStickPanel;
    
    /**
     * Constructs a new GameBoard for the specified game.
     * 
     * @param game The game to display
     */
    public GameBoard(Game game) {
        this.game = game;
        this.board = game.getBoard();
        this.selectedPieces = new ArrayList<>();
        this.canSelectPieces = false;
        this.possibleMovePositions = new HashMap<>();
        
        // Set fixed board dimensions
        setPreferredSize(new Dimension(BOARD_SIZE, BOARD_HEIGHT));
        setMinimumSize(new Dimension(BOARD_SIZE, BOARD_HEIGHT));
        setBackground(new Color(245, 235, 215)); // Light beige background
        setLayout(null); // Use absolute positioning
        
        // Initialize player areas - support up to 4 players
        homeAreas = new Rectangle[4];
        
        // Position all player home areas on the left side, vertically stacked
        int homeSize = 130; // Player area size
        int leftMargin = 40;
        int topMargin = 120;
        int verticalSpacing = 20;
        
        // Place all player boxes on the left side
        homeAreas[0] = new Rectangle(leftMargin, topMargin, homeSize, homeSize); // Player 1 (top)
        homeAreas[1] = new Rectangle(leftMargin, topMargin + homeSize + verticalSpacing, homeSize, homeSize); // Player 2 (second)
        homeAreas[2] = new Rectangle(leftMargin, topMargin + 2*(homeSize + verticalSpacing), homeSize, homeSize); // Player 3 (third)
        homeAreas[3] = new Rectangle(leftMargin, topMargin + 3*(homeSize + verticalSpacing), homeSize, homeSize); // Player 4 (bottom)
        
        // Create pulse animation timer
        pulseTimer = new Timer(50, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                pulseCounter++;
                if (pulseCounter > 10000) {
                    pulseCounter = 0;
                }
                repaint();
            }
        });
        pulseTimer.start();
        
        // Add mouse listener
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                handleMouseClick(e.getX(), e.getY());
            }
        });
        
        // Add action prompt label
        currentActionLabel = new JLabel("", JLabel.CENTER);
        currentActionLabel.setFont(new Font("SansSerif", Font.BOLD, 18));
        currentActionLabel.setForeground(new Color(80, 50, 20));
        currentActionLabel.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(new Color(200, 150, 100), 2),
            BorderFactory.createEmptyBorder(10, 15, 10, 15)
        ));
        currentActionLabel.setBackground(new Color(255, 250, 220, 240));
        currentActionLabel.setOpaque(true);
        currentActionLabel.setBounds((BOARD_SIZE - 550)/2, 10, 550, 45);
        add(currentActionLabel);
        
        // Update action prompt
        updateCurrentActionLabel();
        
        // Add movement hint label
        movementHintLabel = new JLabel("", JLabel.CENTER);
        movementHintLabel.setFont(new Font("SansSerif", Font.BOLD, 16));
        movementHintLabel.setForeground(new Color(120, 0, 0));
        movementHintLabel.setBackground(new Color(255, 255, 220, 240));
        movementHintLabel.setOpaque(true);
        movementHintLabel.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(new Color(200, 150, 100), 2),
            BorderFactory.createEmptyBorder(8, 10, 8, 10)
        ));
        movementHintLabel.setBounds((BOARD_SIZE - 550)/2, 60, 550, 40);
        movementHintLabel.setVisible(false);
        add(movementHintLabel);
    }

    /**
     * 获取YutStickPanel的引用
     */
    private YutStickPanel getYutStickPanel() {
        if (yutStickPanel != null) {
            return yutStickPanel;
        }

        Container parent = getParent();
        while (parent != null && !(parent instanceof JFrame)) {
            parent = parent.getParent();
        }
        
        if (parent instanceof JFrame) {
            JFrame frame = (JFrame) parent;
            // 首先在内容面板中直接搜索
            for (Component comp : frame.getContentPane().getComponents()) {
                if (comp instanceof YutStickPanel) {
                    yutStickPanel = (YutStickPanel) comp;
                    return yutStickPanel;
                }
                // 如果是面板，搜索其子组件
                if (comp instanceof JPanel) {
                    JPanel panel = (JPanel) comp;
                    for (Component innerComp : panel.getComponents()) {
                        if (innerComp instanceof YutStickPanel) {
                            yutStickPanel = (YutStickPanel) innerComp;
                            return yutStickPanel;
                        }
                    }
                }
            }
            
            // 如果还没找到，尝试在整个窗口中搜索
            Component[] components = frame.getContentPane().getComponents();
            for (Component comp : components) {
                if (comp instanceof Container) {
                    YutStickPanel panel = findYutStickPanel((Container) comp);
                    if (panel != null) {
                        yutStickPanel = panel;
                        return yutStickPanel;
                    }
                }
            }
        }
        
        System.out.println("Warning: YutStickPanel not found in the component hierarchy");
        return null;
    }

    /**
     * 递归搜索YutStickPanel
     */
    private YutStickPanel findYutStickPanel(Container container) {
        for (Component comp : container.getComponents()) {
            if (comp instanceof YutStickPanel) {
                return (YutStickPanel) comp;
            }
            if (comp instanceof Container) {
                YutStickPanel panel = findYutStickPanel((Container) comp);
                if (panel != null) {
                    return panel;
                }
            }
        }
        return null;
    }
    
    /**
     * 创建骰子结果选择面板
     */
    private void createRollSelectionPanel() {
        rollSelectionPanel = new JPanel();
        rollSelectionPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 5));
        rollSelectionPanel.setBackground(new Color(255, 255, 220, 250));
        rollSelectionPanel.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(new Color(200, 150, 100), 3),
                BorderFactory.createEmptyBorder(10, 10, 10, 10)
        ));
        
        // Center the panel on the board
        int panelWidth = 320;
        int panelHeight = 220;
        rollSelectionPanel.setBounds((BOARD_SIZE - panelWidth) / 2, 140, panelWidth, panelHeight);
        rollSelectionPanel.setVisible(false);
        add(rollSelectionPanel);
    }
    
    /**
     * 显示可用的骰子结果选择
     */
    private void showRollSelectionOptions() {
        List<Integer> availableRolls = game.getAvailableRolls();
        
        System.out.println("Showing roll selection options. Available rolls: " + availableRolls);
        
        // 如果没有可用的掷骰结果，直接返回
        if (availableRolls.isEmpty()) {
            System.out.println("No available roll results.");
            return;
        }
        
        // 如果只有一个掷骰结果，自动使用它
        if (availableRolls.size() == 1) {
            System.out.println("Only one roll available. Automatically using it: " + availableRolls.get(0));
            int selectedRoll = availableRolls.get(0);
            game.selectRoll(selectedRoll);
            waitingForRollSelection = false;
            updateAfterRollSelection(selectedRoll);
            return;
        }
        
        // 创建一个新的选择对话框，而不是复用之前可能有问题的面板
        JDialog rollSelectionDialog = new JDialog((Frame)SwingUtilities.getWindowAncestor(this), "Choose Roll Result", true);
        rollSelectionDialog.setLayout(new BorderLayout());
        rollSelectionDialog.setSize(300, 200);
        rollSelectionDialog.setLocationRelativeTo(this);
        rollSelectionDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        
        JPanel contentPanel = new JPanel(new BorderLayout(10, 10));
        contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        contentPanel.setBackground(new Color(246, 240, 220));
        
        // 标题标签
        JLabel titleLabel = new JLabel("Choose Your Roll:", JLabel.CENTER);
        titleLabel.setFont(new Font("SansSerif", Font.BOLD, 18));
        titleLabel.setForeground(new Color(120, 60, 0));
        contentPanel.add(titleLabel, BorderLayout.NORTH);
        
        // 按钮面板
        JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 5, 5));
        buttonPanel.setOpaque(false);
        
        // 为每个可用的掷骰结果创建按钮
        for (Integer roll : availableRolls) {
            JButton button = new JButton(roll + " - " + game.getRollName(roll));
            button.setFont(new Font("SansSerif", Font.BOLD, 14));
            
            // 为不同的掷骰值设置不同的颜色
            Color buttonColor;
            switch(roll) {
                case YutSet.MO: buttonColor = new Color(255, 200, 100); break; // 橙色表示MO
                case YutSet.YUT: buttonColor = new Color(200, 240, 120); break; // 淡绿色表示YUT
                case YutSet.GEOL: buttonColor = new Color(120, 200, 255); break; // 淡蓝色表示GEOL
                case YutSet.GAE: buttonColor = new Color(255, 180, 180); break; // 淡红色表示GAE
                case YutSet.DO: buttonColor = new Color(200, 200, 200); break; // 浅灰色表示DO
                case YutSet.BACKDO: buttonColor = new Color(255, 150, 150); break; // 红色表示BACKDO
                default: buttonColor = new Color(230, 230, 230);
            }
            button.setBackground(buttonColor);
            button.setFocusPainted(false);
            button.setBorder(BorderFactory.createRaisedBevelBorder());
            
            // 添加动作监听器处理选择
            final int rollValue = roll;
            button.addActionListener(e -> {
                System.out.println("Roll selected: " + rollValue);
                game.selectRoll(rollValue);
                waitingForRollSelection = false;
                rollSelectionDialog.dispose();
                updateAfterRollSelection(rollValue);
            });
            
            buttonPanel.add(button);
        }
        
        JScrollPane scrollPane = new JScrollPane(buttonPanel);
        scrollPane.setBorder(null);
        scrollPane.setOpaque(false);
        scrollPane.getViewport().setOpaque(false);
        contentPanel.add(scrollPane, BorderLayout.CENTER);
        
        rollSelectionDialog.add(contentPanel);
        
        // 显示对话框
        waitingForRollSelection = true;
        currentActionLabel.setText("Select which roll result to use");
        
        // 确保对话框在EDT线程中显示，避免UI冻结
        SwingUtilities.invokeLater(() -> {
            rollSelectionDialog.setVisible(true);
        });
    }
    
    /**
     * 更新UI以反映用户选择的掷骰子结果
     * @param selectedRoll 选择的掷骰子结果
     */
    private void updateAfterRollSelection(int selectedRoll) {
        System.out.println("Using roll: " + selectedRoll + " (" + game.getRollName(selectedRoll) + ")");
        
        // 启用棋子选择
        canSelectPieces = true;
        
        // 更新提示信息
        String rollName = game.getRollName(selectedRoll);
        movementHintLabel.setText("Using " + rollName + " (" + selectedRoll + ") - Select a piece to move");
        movementHintLabel.setVisible(true);
        
        // 更新动作提示
        currentActionLabel.setText("Select a piece to move " + selectedRoll + " spaces");
        
        // 计算可能的移动位置
        calculatePossibleMoves(selectedRoll);
        
        // 显示提示
        showingHints = true;
        
        repaint();
    }
    
    /**
     * Gets the game instance associated with this board.
     * 
     * @return The game instance
     */
    public Game getGame() {
        return game;
    }
    
    /**
     * Updates the game board after a roll.
     * 
     * @param rollResult The result of the roll
     */
    public void updateAfterRoll(int rollResult) {
        System.out.println("\n=== Update After Roll ===");
        System.out.println("Roll result: " + rollResult + " (" + game.getRollName(rollResult) + ")");
        
        // Update game state
        game.handleThrowResult(rollResult);
        
        // If all pieces are at home and we got BackDo, automatically end turn
        if (rollResult == YutSet.BACKDO && allPiecesAtHome(game.getCurrentPlayer())) {
            System.out.println("Got BackDo with all pieces at home, automatically ending turn");
            handleEndTurn();
            return;
        }
        
        // Enable piece selection
        canSelectPieces = true;
        
        // Calculate possible moves
        calculatePossibleMoves(rollResult);
        
        // Show movement hint
        showMovementHint(rollResult);
        
        // Update current action label
        updateCurrentActionLabel();
        
        // Disable Yut stick panel until move is completed
        YutStickPanel yutPanel = getYutStickPanel();
        if (yutPanel != null) {
            yutPanel.setEnabled(false);
        }
        
        repaint();
    }
    
    /**
     * Shows a movement hint message based on the roll result.
     * 
     * @param rollResult The result of the roll
     */
    private void showMovementHint(int rollResult) {
        // 确保label已初始化
        if (movementHintLabel == null) {
            return;
        }
        
        String rollName = "";
        switch (rollResult) {
            case YutSet.DO: rollName = "Do"; break;
            case YutSet.GAE: rollName = "Gae"; break;
            case YutSet.GEOL: rollName = "Geol"; break;
            case YutSet.YUT: rollName = "Yut"; break;
            case YutSet.MO: rollName = "Mo"; break;
            default: rollName = "Unknown";
        }
        
        movementHintLabel.setText("You rolled a " + rollName + " (" + rollResult + ") - Select a piece to move");
        movementHintLabel.setVisible(true);
    }
    
    /**
     * Calculates and stores the possible move positions for the current roll.
     * 
     * @param rollResult The result of the roll
     */
    private void calculatePossibleMoves(int rollResult) {
        possibleMovePositions.clear();
        Player currentPlayer = game.getCurrentPlayer();
        
        for (Piece piece : currentPlayer.getPieces()) {
            if (game.canMove(piece, rollResult)) {
                int currentPos = piece.getPosition();
                
                // Check if at a junction point
                if (board.isJunction(currentPos)) {
                    Map<Board.PathType, Integer> paths = board.getAvailablePaths(currentPos);
                    for (Integer nextPos : paths.values()) {
                        possibleMovePositions.put(nextPos, true);
                    }
                } else {
                    // Get next position using default path
                    int nextPos = board.getNextPosition(currentPos, rollResult);
                    possibleMovePositions.put(nextPos, true);
                }
            }
        }
    }
    
    /**
     * Moves a piece for the current player.
     * 
     * @param piece The piece to move
     */
    private void movePieceWithAnimation(Piece piece) {
        if (piece == null) return;
        
        // Skip animation and directly move the piece
        if (!game.hasRolled()) {
            currentActionLabel.setText("You need to roll the Yut sticks first before moving!");
            selectedPieces.clear();
            repaint();
            return;
        }
        
        // Record starting position
        final int startPosition = piece.getPosition();
        
        // Calculate new position based on roll value
        int rollValue = game.getCurrentRoll();
        final int endPosition;
        
        // Special handling for movement from home position
        if (startPosition == -1) {
            endPosition = board.getNextPosition(-1, rollValue);
        } else {
            // Check if we're at a junction point (5, 10, or 15)
            if (startPosition == 5 || startPosition == 10 || startPosition == 15) {
                Map<Board.PathType, Integer> paths = board.getAvailablePaths(startPosition);
                if (!paths.isEmpty()) {
                    Board.PathType chosenPath = showPathSelectionDialog(paths);
                    if (chosenPath != null) {
                        endPosition = board.getNextPosition(startPosition, rollValue, chosenPath);
                    } else {
                        endPosition = startPosition; // Stay at current position if no path chosen
                    }
                } else {
                    endPosition = board.getNextPosition(startPosition, rollValue);
                }
            } else {
                // Normal movement
                endPosition = board.getNextPosition(startPosition, rollValue);
            }
        }
        
        System.out.println("Moving " + piece.getOwner().getName() + "'s piece #" + (piece.getId() + 1) + 
                         " from position " + startPosition + " to " + endPosition + 
                         " using roll " + rollValue + " (" + game.getRollName(rollValue) + ")");
        
        // Disable interaction until animation ends
        canSelectPieces = false;
        showingHints = false;
        
        // Complete the actual move
        boolean success = game.movePiece(piece, game.getCurrentRoll());
        
        if (!success) {
            currentActionLabel.setText("Move failed! Make sure you've rolled the Yut sticks first.");
            // Restore UI state
            canSelectPieces = true;
            selectedPieces.clear();
        } else {
            // Add flash effect to show move completion
            addMoveCompletionEffect(endPosition);
            // Update UI state - this will handle re-throws in case of captures
            updateUIAfterMove();
        }
        
        repaint();
    }
    
    /**
     * Updates UI elements after a move
     */
    private void updateUIAfterMove() {
        // Clear selection
        selectedPieces.clear();
        possibleMovePositions.clear();
        showingHints = false;
        
        // Reset movement hint
        if (movementHintLabel != null) {
            movementHintLabel.setText("");
            movementHintLabel.setVisible(false);
        }
        
        // Get the last roll value
        int lastRoll = game.getCurrentRoll();
        
        // Update YutStickPanel state
        YutStickPanel yutPanel = getYutStickPanel();
        if (yutPanel != null) {
            // First complete the current move
            if (!game.getAvailableRolls().isEmpty()) {
                yutPanel.setEnabled(false);
                yutPanel.updateMessage("Select a piece to move");
            }
            // Then check if player gets another throw (Yut or Mo)
            else if (lastRoll == YutSet.YUT || lastRoll == YutSet.MO) {
                yutPanel.setEnabled(true);
                yutPanel.setThrowEnabled(true);
                String rollName = lastRoll == YutSet.YUT ? "Yut" : "Mo";
                yutPanel.updateMessage("You rolled " + rollName + "! Throw again!");
                game.setHasRolled(false);  // Reset for next throw
            }
            // If no more rolls and no re-throw, enable End Turn
            else if (game.getAvailableRolls().isEmpty()) {
                yutPanel.setEnabled(true);
                yutPanel.updateMessage("Click 'End Turn' to finish");
            }
        }
        
        // Update current action label
        updateCurrentActionLabel();
        
        repaint();
    }
    
    /**
     * 尝试获取YutStickPanel的引用以便更新投掷按钮状态
    /**
     * Gets the piece at the specified coordinates (if any).
     * Used for clicking detection.
     * 
     * @param x The x-coordinate
     * @param y The y-coordinate
     * @return The piece at the specified coordinates, or null if none
     */
    private Piece getPieceAt(int x, int y) {
        List<Piece> allPieces = new ArrayList<>();
        for (Player player : game.getPlayers()) {
            allPieces.addAll(player.getPieces());
        }
        
        // 增加棋子检测范围以适应放大后的棋盘
        int hitRadius = PIECE_SIZE * 2; // 显著增加检测范围
        
        for (Piece piece : allPieces) {
            Point piecePos = getPieceDrawPosition(piece);
            if (piecePos != null) {
                // Use distance to check if click was on piece
                double distance = Math.sqrt(
                    Math.pow(piecePos.x - x, 2) + 
                    Math.pow(piecePos.y - y, 2)
                );
                
                if (distance <= hitRadius) {
                    // For stacked pieces, always return the stack leader
                    if (piece.isStacked() && !piece.hasStackedPieces()) {
                        return piece.getStackLeader();
                    }
                    return piece;
                }
            }
        }
        
        // 检查点击是否在家中区域（玩家家里的棋子）
        for (int playerIndex = 0; playerIndex < homeAreas.length; playerIndex++) {
            if (playerIndex >= game.getPlayers().size()) continue;
            
            Rectangle homeArea = homeAreas[playerIndex];
            if (homeArea.contains(x, y)) {
                Player player = game.getPlayers().get(playerIndex);
                
                // 获取这个玩家在家中的所有棋子
                List<Piece> homePieces = new ArrayList<>();
                for (Piece piece : player.getPieces()) {
                    if (piece.getPosition() == -1 && !piece.isStacked()) {
                        homePieces.add(piece);
                    }
                }
                
                // 如果有多个棋子在家，循环点击位置以选择不同的棋子
                if (!homePieces.isEmpty()) {
                    // 计算点击位置相对于家区域的比例
                    double relX = (x - homeArea.x) / (double)homeArea.width;
                    double relY = (y - homeArea.y) / (double)homeArea.height;
                    
                    // 基于点击位置选择棋子
                    int index = 0;
                    if (relX < 0.5) {
                        if (relY < 0.5) index = 0;  // 左上
                        else index = 2;              // 左下
                    } else {
                        if (relY < 0.5) index = 1;  // 右上
                        else index = 3;              // 右下
                    }
                    
                    // 确保索引在有效范围内
                    index = Math.min(index, homePieces.size() - 1);
                    return homePieces.get(index);
                }
            }
        }
        
        return null;
    }
    
    /**
     * Gets the drawing position for a piece on the board.
     * 
     * @param piece The piece to get position for
     * @return The position to draw the piece at, or null if off board
     */
    private Point getPieceDrawPosition(Piece piece) {
        // Position validation
        int position = piece.getPosition();
        
        // Check if piece is on the board
        if (position >= 0 && position <= 30) {
            // 获取正常在棋盘上的棋子位置
            if (position < 31) {
                // Get position from board
                Point boardPos = board.getCoordinate(position);
                if (boardPos == null) {
                    System.out.println("Warning: No coordinate found for position " + position);
                    return null;
                }
                
                // Scale and translate coordinates
                int width = getWidth();
                int height = getHeight();
                int leftPadding = 200; // Space for left side player areas
                int gameAreaX = leftPadding + (width - leftPadding - GAME_AREA_SIZE) / 2;
                int gameAreaY = 120; // 减小顶部空间，将棋盘上移
                
                // Use consistent scaling factors
                float scaleX = GAME_AREA_SIZE / 800.0f; 
                float scaleY = GAME_AREA_SIZE / 800.0f;
                
                // Apply scaling and translation
                int scaledX = gameAreaX + (int)(boardPos.x * scaleX);
                int scaledY = gameAreaY + (int)(boardPos.y * scaleY);
                
                // Calculate offset for stacked pieces
                int stackIndex = 0;
                if (piece.hasStackedPieces()) {
                    stackIndex = 0; // Leader is at index 0
                } else if (piece.isStacked()) {
                    // Find index in stack
                    Piece leader = piece.getStackLeader();
                    if (leader != null && leader.getStackedPieces() != null) {
                        stackIndex = leader.getStackedPieces().indexOf(piece) + 1;
                    }
                }
                
                // Apply offset for stacked pieces
                int offsetX = STACK_OFFSETS_X[stackIndex % STACK_OFFSETS_X.length];
                int offsetY = STACK_OFFSETS_Y[stackIndex % STACK_OFFSETS_Y.length];
                
                return new Point(scaledX + offsetX, scaledY + offsetY);
            }
        }
        
        // For pieces not on the board (at home)
        if (position == -1) {
            return null;  // Handled by drawPieceAtHome
        }
        
        // For pieces that have reached the end
        if (position == 30) {
            int playerIndex = 0;
            for (Player player : game.getPlayers()) {
                if (player == piece.getOwner()) {
                    break;
                }
                playerIndex++;
            }
            
            Rectangle homeArea = homeAreas[playerIndex];
            return new Point(homeArea.x + homeArea.width / 2, homeArea.y + homeArea.height / 2);
        }
        
        return null;
    }
    
    /**
     * Handles mouse clicks on the game board.
     * 
     * @param x The x-coordinate of the click
     * @param y The y-coordinate of the click
     */
    private void handleMouseClick(int x, int y) {
        // If waiting for roll selection, ignore board clicks
        if (waitingForRollSelection) {
            return;
        }
        
        // Get the clicked piece (if any)
        Piece clickedPiece = getPieceAt(x, y);
        
        // Debug info about the click
        System.out.println("Mouse click at (" + x + ", " + y + "), canSelectPieces: " + canSelectPieces + 
                          ", current player: " + game.getCurrentPlayer().getName());
        if (clickedPiece != null) {
            System.out.println("Clicked on piece: " + clickedPiece.getId() + " of player " + 
                              clickedPiece.getOwner().getName());
        }
        
        // If we can select pieces and a piece was clicked
        if (canSelectPieces && clickedPiece != null) {
            handlePieceSelection(clickedPiece);
        }
        // If we have selected pieces and clicked a valid move position
        else if (!selectedPieces.isEmpty()) {
            // Get the position that was clicked
            Point clickPoint = new Point(x, y);
            
            // Calculate scaling parameters - ensure consistency with drawEnhancedBoard
            int width = getWidth();
            int height = getHeight();
            int leftPadding = 200; // Space for left side player areas
            int gameAreaX = leftPadding + (width - leftPadding - GAME_AREA_SIZE) / 2;
            int gameAreaY = 120; // Match value in drawEnhancedBoard
            
            // Use consistent scaling factors
            float scaleX = GAME_AREA_SIZE / 800.0f;
            float scaleY = GAME_AREA_SIZE / 800.0f;
            
            // Get original positions and apply scaling
            Map<Integer, Point> originalPositions = board.getPositions();
            
            // Find if we clicked near a valid position
            for (Map.Entry<Integer, Point> entry : originalPositions.entrySet()) {
                Point originalPos = entry.getValue();
                if (originalPos != null) {
                    // Apply scaling and translation
                    int scaledX = gameAreaX + (int)(originalPos.x * scaleX);
                    int scaledY = gameAreaY + (int)(originalPos.y * scaleY);
                    Point scaledPos = new Point(scaledX, scaledY);
                    
                    if (clickPoint.distance(scaledPos) < POSITION_SIZE) {
                        int targetPosition = entry.getKey();
                        
                        // Check if this is a valid move position
                        if (possibleMovePositions.containsKey(targetPosition)) {
                            // Move the selected piece(s) to this position
                            for (Piece piece : selectedPieces) {
                                if (targetPosition == 1 && piece.getPosition() == -1) {
                                    // Special case for moving from home to start
                                    piece.setPosition(1);
                                } else {
                                    // Normal movement
                                    piece.setPosition(targetPosition);
                                }
                            }
                            
                            // Clear selection and update display
                            selectedPieces.clear();
                            possibleMovePositions.clear();
                            showingHints = false;
                            canSelectPieces = false;
                            
                            // Update the game state
                            game.finishMove();
                            
                            // Repaint the board
                            repaint();
                            break;
                        }
                    }
                }
            }
        }
    }
    
    /**
     * 显示错误提示信息
     */
    private void showErrorHint(String message) {
        if (currentActionLabel != null) {
            currentActionLabel.setText(message);
            currentActionLabel.setForeground(Color.RED);
            currentActionLabel.setVisible(true);
            
            // Auto-hide error after 3 seconds
            Timer timer = new Timer(3000, e -> {
                currentActionLabel.setVisible(false);
            });
            timer.setRepeats(false);
            timer.start();
        }
        System.out.println("Error hint: " + message);
    }
    
    /**
     * Handles the selection of a piece.
     * 
     * @param piece The piece that was clicked
     */
    private void handlePieceSelection(Piece piece) {
        // Check if it's the current player's piece
        if (piece.getOwner() != game.getCurrentPlayer()) {
            System.out.println("Cannot select piece - belongs to player " + piece.getOwner().getName() + 
                           ", but current player is " + game.getCurrentPlayer().getName());
            showErrorHint("It's " + game.getCurrentPlayer().getName() + "'s turn now");
            return;
        }

        // Debug info - log piece selection details
        System.out.println("Selected piece: #" + (piece.getId() + 1) + 
                           " of player " + piece.getOwner().getName() + 
                           " at position " + piece.getPosition());
        
        // Get current roll value and position
        int rollValue = game.getCurrentRoll();
        if (rollValue == 0 && !game.getAvailableRolls().isEmpty()) {
            rollValue = game.getAvailableRolls().get(0);
        }
        
        if (rollValue == 0) {
            showErrorHint("Please roll the Yut sticks first");
            return;
        }
        
        int currentPosition = piece.getPosition();
        
        // Home position - special case
        if (currentPosition == -1) {
            if (rollValue == YutSet.BACKDO) {
                showErrorHint("Cannot move backward from home. Choose another piece.");
                return;
            }
            boolean success = game.movePiece(piece, rollValue);
            if (success) {
                addMoveCompletionEffect(piece.getPosition());
                updateUIAfterMove();
            }
            return;
        }

        // Check if we will pass through position 5
        boolean willPassFive = false;
        int tempPos = currentPosition;
        int remainingSteps = rollValue;
        int stepsToFive = 0;
        
        // Only check for passing through 5 if we're not already at position 5
        if (currentPosition != 5) {
            // Simulate the movement to check if we'll pass through 5
            while (remainingSteps > 0) {
                int nextPos = board.getNextPosition(tempPos, 1);
                if (nextPos == 5) {
                    willPassFive = true;
                    stepsToFive = rollValue - remainingSteps + 1;
                    break;
                }
                tempPos = nextPos;
                remainingSteps--;
            }
        }
        
        // Handle movement based on position and path options
        if (currentPosition == 5 || currentPosition == 10 || currentPosition == 15) {
            // Show path selection dialog for junction points
            Map<Board.PathType, Integer> paths = board.getAvailablePaths(currentPosition);
            if (!paths.isEmpty()) {
                Board.PathType chosenPath = showPathSelectionDialog(paths);
                if (chosenPath != null) {
                    boolean success = game.movePiece(piece, rollValue, chosenPath);
                    if (success) {
                        addMoveCompletionEffect(piece.getPosition());
                        updateUIAfterMove();
                    } else {
                        showErrorHint("Failed to move piece");
                    }
                }
            }
        } else if (willPassFive) {
            // First move to position 5
            boolean moveToFive = game.movePiece(piece, stepsToFive);
            if (moveToFive) {
                // Show path selection dialog at position 5
                Map<Board.PathType, Integer> paths = board.getAvailablePaths(5);
                if (!paths.isEmpty()) {
                    Board.PathType chosenPath = showPathSelectionDialog(paths);
                    if (chosenPath != null) {
                        // Move the remaining steps using the chosen path
                        boolean success = game.movePiece(piece, rollValue - stepsToFive, chosenPath);
                        if (success) {
                            addMoveCompletionEffect(piece.getPosition());
                            updateUIAfterMove();
                        } else {
                            showErrorHint("Failed to complete the movement");
                        }
                    }
                }
            } else {
                showErrorHint("Failed to move to position 5");
            }
        } else {
            // Normal movement for other cases
            boolean success = game.movePiece(piece, rollValue);
            if (success) {
                addMoveCompletionEffect(piece.getPosition());
                updateUIAfterMove();
            } else {
                showErrorHint("Move failed. Make sure you've rolled the Yut sticks first.");
                canSelectPieces = true;
                selectedPieces.clear();
            }
        }
        
        repaint();
    }
    
    /**
     * 显示移动提示信息
     */
    private void updateMovementHint(Piece piece, int targetPosition) {
        if (movementHintLabel == null) {
                    return;
                }
        
        Player player = piece.getOwner();
        String playerName = player.getName();
        String rollName = game.getRollName(game.getCurrentRoll());
        
        StringBuilder message = new StringBuilder();
        message.append(playerName)
               .append(" - Move ");
        
        // 棋子状态描述
        if (piece.getPosition() == -1) {
            message.append("from Home");
        } else {
            message.append("from position ").append(piece.getPosition());
        }
        
        // 目标位置描述
        message.append(" to position ").append(targetPosition);
        message.append(" (").append(rollName).append(")");
        
        // 如果是堆叠移动，添加额外信息
        if (piece.hasStackedPieces()) {
            message.append(" - Moving stack of ").append(piece.getStackedPieces().size() + 1).append(" pieces");
        }
        
        // 显示提示
        movementHintLabel.setText(message.toString());
        movementHintLabel.setVisible(true);
    }
    
    /**
     * 在移动完成后添加闪光效果
     */
    private void addMoveCompletionEffect(int position) {
        // Create a more visible effect at the end position
        final int maxRadius = 70;
        final int steps = 15;
        
        if (position == 30) {
            // 在玩家区域添加效果
            int playerIndex = 0;
            for (Player player : game.getPlayers()) {
                if (player == game.getCurrentPlayer()) {
                    break;
                }
                playerIndex++;
            }
            Rectangle homeArea = homeAreas[playerIndex];
            final Point pos = new Point(homeArea.x + homeArea.width / 2, homeArea.y + homeArea.height / 2);
            
            // Store this position for animation
            currentEffectPosition = pos;
            currentEffectRadius = 0;
            maxEffectRadius = maxRadius;
            effectActive = true;
            
            // Create the animation timer
            Timer effectTimer = new Timer(50, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    currentEffectRadius += maxRadius / steps;
                    if (currentEffectRadius >= maxRadius) {
                        ((Timer)e.getSource()).stop();
                        effectActive = false;
                    } 
                    repaint(pos.x - maxRadius, pos.y - maxRadius, maxRadius * 2, maxRadius * 2);
                }
            });
            
            effectTimer.start();
            return;
        }
        
        // For pieces on the board
        Point boardPos = board.getCoordinate(position);
        if (boardPos == null) return;
        
        // Calculate scaled coordinates to match the board display
        int width = getWidth();
        int height = getHeight();
        int leftPadding = 200; // Space for left side player areas
        int gameAreaX = leftPadding + (width - leftPadding - GAME_AREA_SIZE) / 2;
        int gameAreaY = 120; // Match value in drawEnhancedBoard
        
        // Use consistent scaling factors
        float scaleX = GAME_AREA_SIZE / 800.0f; 
        float scaleY = GAME_AREA_SIZE / 800.0f;
        
        // Scale the position to match the actual drawing area
        final Point scaledPos = new Point(
            gameAreaX + (int)(boardPos.x * scaleX),
            gameAreaY + (int)(boardPos.y * scaleY)
        );
        
        // Store this position for animation
        currentEffectPosition = scaledPos;
        currentEffectRadius = 0;
        maxEffectRadius = maxRadius;
        effectActive = true;
        
        // Create the animation timer
        Timer effectTimer = new Timer(50, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                currentEffectRadius += maxRadius / steps;
                if (currentEffectRadius >= maxRadius) {
                    ((Timer)e.getSource()).stop();
                    effectActive = false;
                } 
                // Only repaint the affected area for efficiency
                repaint(scaledPos.x - maxRadius, scaledPos.y - maxRadius, maxRadius * 2, maxRadius * 2);
            }
        });
        
        effectTimer.start();
        
        // Draw the effect immediately for the first frame
        Graphics2D g2d = (Graphics2D)getGraphics();
        if (g2d != null) {
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setColor(new Color(255, 215, 0, 150)); // Gold color with transparency
            g2d.fillOval(scaledPos.x - 20, scaledPos.y - 20, 40, 40);
            g2d.dispose();
        }
    }
    
    /**
     * 重置游戏板状态
     */
    public void reset() {
        selectedPieces.clear();
        canSelectPieces = false;
        showingHints = false;
        waitingForRollSelection = false;
        movementHintLabel.setVisible(false);
        possibleMovePositions.clear();
        lastLoggedPositions.clear(); // 清空位置日志缓存
        
        // Reset animation states
        effectActive = false;
        currentEffectPosition = null;
        currentEffectRadius = 0;
        
        // Reset current action label
        currentActionLabel.setText("Throw the Yut sticks to begin your turn");
        
        repaint();
    }
    
    /**
     * Draws a piece at its home position.
     * 
     * @param g2d The Graphics2D object to draw with
     * @param piece The piece to draw
     * @param index The index of the piece in the home area
     */
    private void drawPieceAtHome(Graphics2D g2d, Piece piece, int index) {
        Player owner = piece.getOwner();
        int playerIndex = game.getPlayers().indexOf(owner);
        
        if (playerIndex < 0 || playerIndex >= homeAreas.length) {
            return;
        }
        
        Rectangle homeArea = homeAreas[playerIndex];
        
        // 2x2 grid layout for 4 pieces
        int gridSize = Math.min(homeArea.width, homeArea.height) / 2 - 15;
        int startX = homeArea.x + 25;
        int startY = homeArea.y + 40; // Room for player name at top
        
        // Calculate piece position (2x2 grid)
        int row = index / 2;
        int col = index % 2;
        
        int xPos = startX + col * gridSize;
        int yPos = startY + row * gridSize;
        
        // Draw piece
        drawPiece(g2d, piece, xPos, yPos);
    }
    
    /**
     * Draws a piece on the board.
     * 
     * @param g2d The Graphics2D object to draw with
     * @param piece The piece to draw
     */
    private void drawPieceOnBoard(Graphics2D g2d, Piece piece) {
        Point drawPos = getPieceDrawPosition(piece);
        if (drawPos != null) {
            // 只在位置变化时记录日志
            if (!lastLoggedPositions.containsKey(piece.getId()) || 
                lastLoggedPositions.get(piece.getId()) != piece.getPosition()) {
                
                System.out.println("Drawing piece on board - ID: " + piece.getId() + 
                                 ", Player: " + piece.getOwner().getName() + 
                                 ", Position: " + piece.getPosition());
                System.out.println("  Drawing at screen coordinates: (" + drawPos.x + "," + drawPos.y + ")");
                
                lastLoggedPositions.put(piece.getId(), piece.getPosition());
            }
            
            drawPiece(g2d, piece, drawPos.x, drawPos.y);
        }
    }
    
    /**
     * Draws a piece with the appropriate styling.
     * 
     * @param g2d The Graphics2D context
     * @param piece The piece to draw
     * @param x The x-coordinate
     * @param y The y-coordinate
     */
    private void drawPiece(Graphics2D g2d, Piece piece, int x, int y) {
        Color pieceColor = piece.getOwner().getColor();
        
        // Save original transform
        AffineTransform original = g2d.getTransform();
        
        // Enable antialiasing for smoother pieces
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        // Draw piece
        g2d.setColor(pieceColor);
        g2d.fillOval(x - PIECE_SIZE / 2, y - PIECE_SIZE / 2, PIECE_SIZE, PIECE_SIZE);
        
        // Draw outline
        g2d.setColor(new Color(50, 50, 50));
        g2d.setStroke(new BasicStroke(2.5f));  // 更粗的边框
        g2d.drawOval(x - PIECE_SIZE / 2, y - PIECE_SIZE / 2, PIECE_SIZE, PIECE_SIZE);
        
        // Highlight selected pieces
        if (selectedPieces.contains(piece)) {
            g2d.setColor(Color.YELLOW);
            g2d.setStroke(new BasicStroke(3));
            g2d.drawOval(x - PIECE_SIZE / 2 - 3, y - PIECE_SIZE / 2 - 3, PIECE_SIZE + 6, PIECE_SIZE + 6);
        }
        
        // Add shine effect
        GradientPaint shine = new GradientPaint(
            x - PIECE_SIZE/3, y - PIECE_SIZE/3, new Color(255, 255, 255, 120),
            x + PIECE_SIZE/3, y + PIECE_SIZE/3, new Color(255, 255, 255, 0)
        );
        g2d.setPaint(shine);
        g2d.fillOval(x - PIECE_SIZE/2 + 2, y - PIECE_SIZE/2 + 2, PIECE_SIZE - 4, PIECE_SIZE - 4);
        
        // Draw piece ID with clearer contrast
        g2d.setColor(Color.WHITE);
        g2d.setFont(new Font("Arial", Font.BOLD, 20));  // 更大的字体
        String id = String.valueOf(piece.getId() + 1);
        FontMetrics fm = g2d.getFontMetrics();
        int textWidth = fm.stringWidth(id);
        int textHeight = fm.getAscent();
        
        // Draw text shadow for contrast
        g2d.setColor(new Color(0, 0, 0, 160));
        g2d.drawString(id, x - textWidth / 2 + 1, y + textHeight / 4 + 1);
        
        // Draw actual number
        g2d.setColor(Color.WHITE);
        g2d.drawString(id, x - textWidth / 2, y + textHeight / 4);
        
        // 新增：在棋子下方添加玩家名称标签
        String playerName = piece.getOwner().getName();
        g2d.setFont(new Font("Arial", Font.BOLD, 10));
        fm = g2d.getFontMetrics();
        int nameWidth = fm.stringWidth(playerName);
        
        // 如果名称太长，进行截断
        if (nameWidth > PIECE_SIZE * 1.5) {
            playerName = playerName.substring(0, 3) + "..";
            nameWidth = fm.stringWidth(playerName);
        }
        
        // 绘制背景框
        g2d.setColor(new Color(255, 255, 255, 200));
        g2d.fillRoundRect(
            x - nameWidth/2 - 3,
            y + PIECE_SIZE/2 + 2,
            nameWidth + 6,
            15,
            5, 5
        );
        
        // 绘制玩家名称
        g2d.setColor(piece.getOwner().getColor().darker());
        g2d.drawString(
            playerName,
            x - nameWidth/2,
            y + PIECE_SIZE/2 + 12
        );
        
        // Restore original transform
        g2d.setTransform(original);
    }
    
    /**
     * Draw the current landing effect animation if active
     */
    private void drawLandingEffect(Graphics2D g2d) {
        if (!effectActive || currentEffectPosition == null) {
            return;
        }
        
        // Create a ripple effect
        float alpha = 1.0f - (float)currentEffectRadius / maxEffectRadius;
        Color effectColor = new Color(255, 215, 0, (int)(alpha * 180));
        g2d.setColor(effectColor);
        
        // Draw the outer circle
        g2d.setStroke(new BasicStroke(3.0f));
        g2d.drawOval(
            currentEffectPosition.x - currentEffectRadius/2,
            currentEffectPosition.y - currentEffectRadius/2,
            currentEffectRadius,
            currentEffectRadius
        );
        
        // Draw inner glow
        if (currentEffectRadius < maxEffectRadius * 0.5f) {
            int innerSize = (int)(maxEffectRadius * 0.5f - currentEffectRadius * 0.5f);
            if (innerSize > 0) {
                g2d.setColor(new Color(255, 215, 0, (int)(alpha * 100)));
                g2d.fillOval(
                    currentEffectPosition.x - innerSize/2,
                    currentEffectPosition.y - innerSize/2,
                    innerSize,
                    innerSize
                );
            }
        }
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        // 避免不必要的重绘
        if (!isVisible()) {
            return;
        }
        
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        // 绘制游戏板
        drawBoard(g2d);
        
        // 绘制棋子
        drawPieces(g2d);
        
        // 绘制提示
        if (showingHints) {
            drawMovementHints(g2d);
        }
    }
    
    /**
     * Draws an enhanced version of the game board.
     * 
     * @param g2d The Graphics2D object to draw with
     */
    private void drawBoard(Graphics2D g2d) {
        // Get current component dimensions
        int width = getWidth();
        int height = getHeight();
        
        // Calculate game area position - shift to the right to make room for player areas
        int leftPadding = 200; // Space for left side player areas
        int gameAreaX = leftPadding + (width - leftPadding - GAME_AREA_SIZE) / 2;
        int gameAreaY = 120; // 减小顶部空间，将棋盘上移
        
        // Draw board background
        g2d.setColor(new Color(245, 235, 215));
        g2d.fillRect(0, 0, width, height);
        
        // Draw game area background
        g2d.setColor(new Color(250, 245, 225));
        g2d.fillRect(gameAreaX - 10, gameAreaY - 10, GAME_AREA_SIZE + 20, GAME_AREA_SIZE + 20);
        
        // Draw board border
        g2d.setColor(new Color(139, 69, 19));
        g2d.setStroke(new BasicStroke(3.0f));
        g2d.drawRect(gameAreaX - 10, gameAreaY - 10, GAME_AREA_SIZE + 20, GAME_AREA_SIZE + 20);
        
        // Add wood texture
        drawWoodTexture(g2d, gameAreaX - 10, gameAreaY - 10, GAME_AREA_SIZE + 20, GAME_AREA_SIZE + 20);
        
        // Draw grid lines
        g2d.setColor(new Color(210, 180, 140, 100));
        g2d.setStroke(new BasicStroke(1.0f));
        
        // Draw horizontal and vertical grid lines
        int cellSize = GAME_AREA_SIZE / 5; 
        for (int i = 0; i <= 5; i++) {
            // Horizontal line
            g2d.drawLine(
                gameAreaX, 
                gameAreaY + i * cellSize, 
                gameAreaX + GAME_AREA_SIZE, 
                gameAreaY + i * cellSize
            );
            
            // Vertical line
            g2d.drawLine(
                gameAreaX + i * cellSize, 
                gameAreaY, 
                gameAreaX + i * cellSize, 
                gameAreaY + GAME_AREA_SIZE
            );
        }
        
        // Calculate scaling factors
        float scaleX = GAME_AREA_SIZE / 800.0f; // 800 is the size used in Board
        float scaleY = GAME_AREA_SIZE / 800.0f;
        
        // Scale all position coordinates
        Map<Integer, Point> positions = board.getPositions();
        Map<Integer, Point> scaledPositions = new HashMap<>();
        
        for (Map.Entry<Integer, Point> entry : positions.entrySet()) {
            Point original = entry.getValue();
            Point scaled = new Point(
                gameAreaX + (int)(original.x * scaleX),
                gameAreaY + (int)(original.y * scaleY)
            );
            scaledPositions.put(entry.getKey(), scaled);
        }
        
        // Draw paths and positions
        drawBoardPaths(g2d, scaledPositions);
        
        // Draw position markers
        for (Map.Entry<Integer, Point> entry : scaledPositions.entrySet()) {
            int position = entry.getKey();
            Point point = entry.getValue();
            
            // Skip positions that shouldn't be drawn
            if (position != 30) {
                boolean isCorner = board.isCorner(position);
                drawBoardPosition(g2d, point.x, point.y, isCorner, position);
            }
        }
        
        // Draw legend in the right side
        drawBoardLegend(g2d);
    }
    
    /**
     * Draws wood texture on the specified rectangle.
     * 
     * @param g2d The graphics context
     * @param x X coordinate
     * @param y Y coordinate
     * @param width Width
     * @param height Height
     */
    private void drawWoodTexture(Graphics2D g2d, int x, int y, int width, int height) {
        // Save original settings
        Composite originalComposite = g2d.getComposite();
        
        // Draw wood grain effect
        Random random = new Random(1234); // Fixed seed for consistent grain
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f));
        g2d.setColor(new Color(139, 69, 19));
        
        int grainCount = height / 4;
        for (int i = 0; i < grainCount; i++) {
            int grainY = y + i * 4 + random.nextInt(3);
            int variance1 = random.nextInt(5) - 2;
            int variance2 = random.nextInt(5) - 2;
            
            g2d.drawLine(x, grainY + variance1, x + width, grainY + variance2);
        }
        
        // Restore original settings
        g2d.setComposite(originalComposite);
    }
    
    /**
     * Draws the paths connecting board positions.
     * 
     * @param g2d The Graphics2D object to draw with
     * @param positions The scaled position coordinates
     */
    private void drawBoardPaths(Graphics2D g2d, Map<Integer, Point> positions) {
        // Set rendering hints for smoother lines
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        
        // Draw main path (brown)
        g2d.setColor(new Color(139, 69, 19));
        g2d.setStroke(new BasicStroke(4f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        drawPathByType(g2d, positions, Board.PathType.MAIN_PATH);
        
        // Draw first diagonal path (blue) - 10 -> 26 -> 27 -> 23 -> 28 -> 29 -> 20
        g2d.setColor(new Color(0, 100, 200));
        g2d.setStroke(new BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        drawStraightDiagonalPath(g2d, positions, new int[]{10, 26, 27, 23, 28, 29, 20});
        
        // Draw second diagonal path (red) - direct line from 5 to 15
        g2d.setColor(new Color(200, 0, 0));
        g2d.setStroke(new BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        Point start = positions.get(5);
        Point end = positions.get(15);
        if (start != null && end != null) {
            g2d.drawLine(start.x, start.y, end.x, end.y);
            drawArrow(g2d, start.x, start.y, end.x, end.y);
        }
    }
    
    private void drawStraightDiagonalPath(Graphics2D g2d, Map<Integer, Point> positions, int[] pathPoints) {
        for (int i = 0; i < pathPoints.length - 1; i++) {
            Point start = positions.get(pathPoints[i]);
            Point end = positions.get(pathPoints[i + 1]);
            
            if (start != null && end != null) {
                // Draw the line segment
                g2d.drawLine(start.x, start.y, end.x, end.y);
                
                // Draw arrow for direction
                if (i < pathPoints.length - 2) {  // Don't draw arrow for last segment
                    drawArrow(g2d, start.x, start.y, end.x, end.y);
                }
            }
        }
    }
    
    private void drawPathByType(Graphics2D g2d, Map<Integer, Point> positions, Board.PathType pathType) {
        Map<Integer, Integer> pathMap = board.getPathMap(pathType);
        if (pathMap == null) return;
        
        for (Map.Entry<Integer, Integer> path : pathMap.entrySet()) {
            Point start = positions.get(path.getKey());
            Point end = positions.get(path.getValue());
            
            if (start != null && end != null) {
                g2d.drawLine(start.x, start.y, end.x, end.y);
                drawArrow(g2d, start.x, start.y, end.x, end.y);
            }
        }
    }
    
    private void drawArrow(Graphics2D g2d, int x1, int y1, int x2, int y2) {
        int arrowSize = 8;  // Slightly smaller arrow
        double angle = Math.atan2(y2 - y1, x2 - x1);
        
        // Calculate arrow head points
        int x3 = (int)(x2 - arrowSize * Math.cos(angle - Math.PI/6));
        int y3 = (int)(y2 - arrowSize * Math.sin(angle - Math.PI/6));
        int x4 = (int)(x2 - arrowSize * Math.cos(angle + Math.PI/6));
        int y4 = (int)(y2 - arrowSize * Math.sin(angle + Math.PI/6));
        
        // Draw arrow head
        g2d.drawLine(x2, y2, x3, y3);
        g2d.drawLine(x2, y2, x4, y4);
    }
    
    /**
     * 绘制棋盘上的位置点，特殊处理不同类型的节点
     */
    private void drawBoardPosition(Graphics2D g2d, int x, int y, boolean isCorner, int pos) {
        int size = isCorner ? POSITION_SIZE + 10 : POSITION_SIZE;
        
        // Special nodes use different colors, center node is larger
        if (pos == 23) {
            size = 55; // Larger center node size
            g2d.setColor(new Color(255, 150, 150)); // Pink color for new center node
        } else if (pos == 24) {
            size = POSITION_SIZE; // Normal size
            g2d.setColor(new Color(255, 215, 0)); // Yellow color
        } else if (pos == 20) {
            // Position 20 is start and end, use gold highlight
            size = 60; // Larger size
            g2d.setColor(new Color(255, 215, 0, 220)); // Gold
        } else if (pos == 22 || pos == 25 || pos == 26 || pos == 28) {
            g2d.setColor(new Color(200, 200, 255)); // Light blue for path nodes
        } else if (pos == 21) {
            g2d.setColor(new Color(255, 230, 180)); // Highlight position 21 to avoid overlap
        } else {
            g2d.setColor(new Color(245, 222, 179)); // Beige for normal nodes
        }
        
        // Draw the position circle
        g2d.fillOval(x - size/2, y - size/2, size, size);
        
        // Draw border
        g2d.setColor(new Color(139, 69, 19)); // Brown for border
        g2d.setStroke(new BasicStroke(2.0f)); // Thinner border
        
        // Add double border for start/end point (position 20)
        if (pos == 20) {
            g2d.drawOval(x - size/2, y - size/2, size, size);
            g2d.setStroke(new BasicStroke(3.0f));
            g2d.setColor(new Color(255, 165, 0)); // Orange outer border
            g2d.drawOval(x - size/2 - 3, y - size/2 - 3, size + 6, size + 6);
            
            // Add "Start/End" label
            g2d.setFont(new Font("Arial", Font.BOLD, 10));
            g2d.setColor(new Color(139, 69, 19));
            g2d.drawString("START/END", x - 30, y + size/2 + 15);
        } else {
            g2d.drawOval(x - size/2, y - size/2, size, size);
        }
        
        // Draw position number
        g2d.setColor(new Color(139, 69, 19)); // Brown for text
        g2d.setFont(new Font("Arial", Font.BOLD, isCorner ? 18 : 16)); // Smaller font
        String number = String.valueOf(pos);
        FontMetrics fm = g2d.getFontMetrics();
        int textX = x - fm.stringWidth(number)/2;
        int textY = y + fm.getAscent()/2;
        g2d.drawString(number, textX, textY);
    }
    
    /**
     * Draws all pieces on the board.
     * 
     * @param g2d The Graphics2D context
     */
    private void drawPieces(Graphics2D g2d) {
        List<Player> players = game.getPlayers();
        
        // Draw all pieces in home area first
        for (Player player : players) {
            List<Piece> homePieces = new ArrayList<>();
            
            // Collect all pieces at home
            for (Piece piece : player.getPieces()) {
                // Check if this piece should be in the home area (position -1 and not stacked)
                if (piece.getPosition() == -1 && !piece.isStacked()) {
                    homePieces.add(piece);
                }
            }
            
            // Sort by ID
            Collections.sort(homePieces, (p1, p2) -> p1.getId() - p2.getId());
            
            // Draw sorted pieces
            for (int i = 0; i < homePieces.size(); i++) {
                drawPieceAtHome(g2d, homePieces.get(i), i);
            }
        }
        
        // Sort board pieces to ensure consistent rendering order
        List<Piece> boardPieces = new ArrayList<>();
        
        // Then draw pieces on the board
        for (Player player : players) {
            for (Piece piece : player.getPieces()) {
                // Only draw pieces on the board (position 0-29, not stacked)
                if (piece.getPosition() >= 0 && piece.getPosition() < 30 && !piece.isStacked()) {
                    boardPieces.add(piece);
                }
            }
        }
        
        // Sort by position to ensure consistent rendering
        Collections.sort(boardPieces, (p1, p2) -> p1.getPosition() - p2.getPosition());
        
        // Draw pieces on the board
        for (Piece piece : boardPieces) {
            // Verify position before drawing
            int position = piece.getPosition();
            if (position >= 0 && position < 30) {
                drawPieceOnBoard(g2d, piece);
                
                // Draw stacked pieces
                if (piece.hasStackedPieces()) {
                    drawStackedPieces(g2d, piece);
                }
            } else {
                System.out.println("WARNING: Skipping piece with invalid position: " + position);
            }
        }
        
        // Draw pieces that finished (reached position 30)
        List<Piece> finishedPieces = new ArrayList<>();
        for (Player player : players) {
            for (Piece piece : player.getPieces()) {
                if (piece.getPosition() == 30 && !piece.isStacked()) {
                    finishedPieces.add(piece);
                }
            }
        }
        
        // Sort finished pieces by ID
        Collections.sort(finishedPieces, (p1, p2) -> p1.getId() - p2.getId());
        
        // Draw finished pieces
        for (Piece piece : finishedPieces) {
            Point finishPos = getPieceDrawPosition(piece);
            if (finishPos != null) {
                drawPiece(g2d, piece, finishPos.x, finishPos.y);
            }
        }
        
        // Only print piece positions once per paintComponent call
        // Don't spam the console with repeated logs
        boolean shouldPrintPiecePositions = false;
        
        if (shouldPrintPiecePositions) {
            System.out.println("---- All Piece Positions ----");
            for (Player player : players) {
                System.out.println(player.getName() + "'s pieces:");
                for (Piece piece : player.getPieces()) {
                    System.out.println("  ID:" + piece.getId() + 
                                     " Position:" + piece.getPosition() +
                                     " Previous:" + piece.getPreviousPosition() + 
                                     " Stacked:" + piece.isStacked() +
                                     " Has stacked:" + piece.hasStackedPieces());
                }
            }
        }
    }
    
    /**
     * Draws stacked pieces.
     * 
     * @param g2d The Graphics2D object to draw with
     * @param leaderPiece The leader piece in the stack
     */
    private void drawStackedPieces(Graphics2D g2d, Piece leaderPiece) {
        // 获取堆叠的棋子
        List<Piece> stackedPieces = leaderPiece.getStackedPieces();
        if (stackedPieces.isEmpty()) {
            return;
        }
        
        // 计算主棋子的位置
        Point leaderPos = getPieceDrawPosition(leaderPiece);
        if (leaderPos == null) {
            return;
        }
        
        // 绘制堆叠的棋子 - 错开一点位置以便能看到所有棋子
        for (int i = 0; i < stackedPieces.size(); i++) {
            Piece stackedPiece = stackedPieces.get(i);
            int offsetIndex = (i % 4); // 循环使用偏移量
            
            // 计算偏移后的位置
            int offsetX = STACK_OFFSETS_X[offsetIndex];
            int offsetY = STACK_OFFSETS_Y[offsetIndex];
            
            // 根据堆叠的深度增加偏移
            int depthOffset = (i / 4) * 2;
            offsetX += depthOffset;
            offsetY += depthOffset;
            
            // 绘制堆叠的棋子
            drawPiece(g2d, stackedPiece, leaderPos.x + offsetX, leaderPos.y + offsetY);
        }
    }
    
    /**
     * Draws player homes with clearer styling.
     */
    private void drawPlayerHomes(Graphics2D g2d) {
        // Get board dimensions
        int width = getWidth();
        int height = getHeight();
        
        // Adjust home areas based on player count
        List<Player> players = game.getPlayers();
        int playerCount = players.size();
        
        // Home dimensions and position constants
        int homeSize = 130;
        int leftMargin = 40;
        int topMargin = 120;
        int verticalSpacing = 20;
        
        // Dynamically adjust home areas for different player counts
        // Always place all player areas on left side vertically
        for (int i = 0; i < playerCount; i++) {
            homeAreas[i] = new Rectangle(leftMargin, topMargin + i*(homeSize + verticalSpacing), homeSize, homeSize);
        }
        
        // Draw player areas
        int playerIndex = 0;
        for (Player player : players) {
            if (playerIndex < homeAreas.length) {
                Rectangle homeArea = homeAreas[playerIndex];
                
                // Draw background
                g2d.setColor(player.getColor());
                g2d.fillRect(homeArea.x, homeArea.y, homeArea.width, homeArea.height);
                
                // Draw border
                g2d.setColor(Color.BLACK);
                g2d.setStroke(new BasicStroke(2f));
                g2d.drawRect(homeArea.x, homeArea.y, homeArea.width, homeArea.height);
                
                // Draw player name at the top of the area
                g2d.setColor(Color.WHITE);
                g2d.setFont(new Font("SansSerif", Font.BOLD, 14));
                String playerName = player.getName();
                FontMetrics fm = g2d.getFontMetrics();
                int textWidth = fm.stringWidth(playerName);
                g2d.drawString(playerName, 
                    homeArea.x + (homeArea.width - textWidth)/2,
                    homeArea.y + 20);
                
                // Show player progress
                String progressText = player.getFinishedPieceCount() + "/" + player.getPieces().size();
                textWidth = fm.stringWidth(progressText);
                g2d.drawString(progressText, 
                    homeArea.x + (homeArea.width - textWidth)/2,
                    homeArea.y + homeArea.height - 10);
                
                playerIndex++;
            }
        }
    }
    
    /**
     * Draws the game over message.
     */
    private void drawGameOverMessage(Graphics2D g2d) {
        Player winner = game.getWinner();
        if (winner == null) {
            return;
        }
        
        // Half-transparent background
        g2d.setColor(new Color(0, 0, 0, 180));
        g2d.fillRect(0, 0, getWidth(), getHeight());
        
        // Adjust message position to account for left player area
        int leftPadding = 200; // Space for left side player areas
        int messageWidth = 500;
        int messageHeight = 300;
        int messageX = leftPadding + (getWidth() - leftPadding - messageWidth) / 2;
        int messageY = (getHeight() - messageHeight) / 2;
        
        // Use winner's color
        Color winnerColor = winner.getColor();
        Color darkerColor = new Color(
            Math.max(0, winnerColor.getRed() - 50),
            Math.max(0, winnerColor.getGreen() - 50),
            Math.max(0, winnerColor.getBlue() - 50),
            240
        );
        
        // Fill background
        g2d.setColor(new Color(255, 255, 255, 240));
        g2d.fillRoundRect(messageX, messageY, messageWidth, messageHeight, 30, 30);
        
        // Add border
        g2d.setStroke(new BasicStroke(5f));
        g2d.setColor(winner.getColor());
        g2d.drawRoundRect(messageX, messageY, messageWidth, messageHeight, 30, 30);
        
        // Pulsing border effect
        float alpha = 0.5f + 0.5f * (float)Math.sin(pulseCounter * 0.3);
        g2d.setColor(new Color(
            255, 255, 255, 
            (int)(alpha * 200)
        ));
        g2d.setStroke(new BasicStroke(2f));
        g2d.drawRoundRect(messageX + 5, messageY + 5, messageWidth - 10, messageHeight - 10, 25, 25);
        
        // Draw "Game Over" title
        g2d.setFont(new Font("Arial", Font.BOLD, 48));
        g2d.setColor(new Color(50, 50, 50));
        String gameOverText = "Game Over!";
        FontMetrics fm = g2d.getFontMetrics();
        int gameOverWidth = fm.stringWidth(gameOverText);
        g2d.drawString(gameOverText, messageX + (messageWidth - gameOverWidth) / 2 + 2, messageY + 80 + 2);
        
        // Draw game over text (with shadow effect)
        g2d.setColor(new Color(245, 245, 245));
        g2d.drawString(gameOverText, messageX + (messageWidth - gameOverWidth) / 2, messageY + 80);
        
        // Draw winner information
        g2d.setFont(new Font("Arial", Font.BOLD, 36));
        String winnerText = winner.getName() + " Wins!";
        fm = g2d.getFontMetrics();
        int winnerWidth = fm.stringWidth(winnerText);
        
        // Draw text shadow
        g2d.setColor(new Color(80, 80, 80));
        g2d.drawString(winnerText, messageX + (messageWidth - winnerWidth) / 2 + 2, messageY + 160 + 2);
        
        // Draw text with winner's color
        g2d.setColor(winner.getColor());
        g2d.drawString(winnerText, messageX + (messageWidth - winnerWidth) / 2, messageY + 160);
        
        // Add restart game hint
        g2d.setFont(new Font("Arial", Font.PLAIN, 20));
        String restartText = "Click 'New Game' to start a new game";
        fm = g2d.getFontMetrics();
        int restartWidth = fm.stringWidth(restartText);
        
        // Draw flashing hint text
        int brightness = 100 + (int)(155 * Math.sin(pulseCounter * 0.2));
        g2d.setColor(new Color(brightness, brightness, brightness));
        g2d.drawString(restartText, messageX + (messageWidth - restartWidth) / 2, messageY + 220);
        
        // Draw victory star decorations
        drawVictoryStars(g2d);
    }
    
    /**
     * 绘制胜利庆祝的星星装饰效果
     */
    private void drawVictoryStars(Graphics2D g2d) {
        // 根据脉冲计数器计算星星的位置和大小
        int numStars = 20;
        Random random = new Random(123); // 固定种子使星星位置保持一致
        
        for (int i = 0; i < numStars; i++) {
            // 计算星星位置 (随机分布在屏幕周围)
            int x = random.nextInt(getWidth());
            int y = random.nextInt(getHeight());
            
            // 计算星星大小 (基于脉冲计数器，使它们闪烁)
            float starPhase = (pulseCounter * 0.2f + i * 0.5f) % 10;
            float starSize = 5 + 15 * Math.abs((starPhase - 5) / 5);
            
            // 计算星星颜色 (随机亮色)
            int r = 150 + random.nextInt(105);
            int g = 150 + random.nextInt(105);
            int b = 150 + random.nextInt(105);
            int alpha = 150 + (int)(105 * Math.sin(starPhase));
            
            Color starColor = new Color(r, g, b, alpha);
            
            // 绘制星星
            drawStar(g2d, x, y, starSize, starColor);
        }
    }
    
    /**
     * 在指定位置绘制一颗星星
     */
    private void drawStar(Graphics2D g2d, int x, int y, float size, Color color) {
        // 创建星星的形状
        int numPoints = 5;
        int[] xPoints = new int[numPoints * 2];
        int[] yPoints = new int[numPoints * 2];
        
        double angle = Math.PI / numPoints;
        double outerRadius = size;
        double innerRadius = size / 2;
        
        for (int i = 0; i < numPoints * 2; i++) {
            double r = (i % 2 == 0) ? outerRadius : innerRadius;
            double theta = i * angle;
            xPoints[i] = x + (int)(r * Math.sin(theta));
            yPoints[i] = y + (int)(r * Math.cos(theta));
        }
        
        // 填充星星
        g2d.setColor(color);
        g2d.fillPolygon(xPoints, yPoints, numPoints * 2);
    }
    
    /**
     * Draws all the positions where the player can possibly move to
     * 
     * @param g2d Graphics2D context
     */
    private void drawPossibleMoves(Graphics2D g2d) {
        if (!showingHints || game.getCurrentPlayer() == null) {
            return;
        }
        
        // Only highlight if there are possible moves and player needs to select a piece
        if (possibleMovePositions.isEmpty() || !canSelectPieces) {
            return;
        }
        
        // 计算缩放参数 - 确保与drawEnhancedBoard方法中使用相同的缩放系数
        int width = getWidth();
        int height = getHeight();
        int leftPadding = 200; // Space for left side player areas
        int gameAreaX = leftPadding + (width - leftPadding - GAME_AREA_SIZE) / 2;
        int gameAreaY = 120; // 确保与drawEnhancedBoard中相同的值
        float scaleX = GAME_AREA_SIZE / 800.0f; 
        float scaleY = GAME_AREA_SIZE / 800.0f;
        
        for (Map.Entry<Integer, Boolean> entry : possibleMovePositions.entrySet()) {
            if (entry.getValue()) {
                int position = entry.getKey();
                Point originalPos = board.getCoordinate(position);
                if (originalPos != null) {
                    // 应用缩放和平移
                    int scaledX = gameAreaX + (int)(originalPos.x * scaleX);
                    int scaledY = gameAreaY + (int)(originalPos.y * scaleY);
                    
                    boolean isCorner = board.isCorner(position);
                    drawBoardPosition(g2d, scaledX, scaledY, isCorner, position);
                }
            }
        }
    }
    
    /**
     * 绘制棋盘图例
     */
    private void drawBoardLegend(Graphics2D g2d) {
        // Get board dimensions and position
        int width = getWidth();
        int height = getHeight();
        int leftPadding = 200; // Space for left side player areas
        int gameAreaX = leftPadding + (width - leftPadding - GAME_AREA_SIZE) / 2;
        int gameAreaY = 120;
        
        // Position legend at the bottom left side of the board
        int legendX = 10; // 移到最左侧
        int legendY = height - 140; // 放在底部
        int legendWidth = 200; // 保持宽度
        int legendHeight = 110;
        
        // Draw semi-transparent background
        g2d.setColor(new Color(245, 245, 245, 200));
        g2d.fillRoundRect(legendX, legendY, legendWidth, legendHeight, 10, 10);
        g2d.setColor(new Color(139, 69, 19));
        g2d.setStroke(new BasicStroke(1.5f));
        g2d.drawRoundRect(legendX, legendY, legendWidth, legendHeight, 10, 10);
        
        // Legend title
        g2d.setColor(new Color(139, 69, 19));
        g2d.setFont(new Font("SansSerif", Font.BOLD, 14));
        g2d.drawString("Path Legend", legendX + 10, legendY + 20);
        
        // Draw legend items
        g2d.setFont(new Font("SansSerif", Font.PLAIN, 12));
        
        // Main path legend
        int itemY = legendY + 40;
        g2d.setColor(new Color(139, 69, 19));
        g2d.setStroke(new BasicStroke(4f));
        g2d.drawLine(legendX + 12, itemY, legendX + 36, itemY);
        g2d.setColor(Color.BLACK);
        g2d.drawString("Main Path", legendX + 46, itemY + 4);
        
        // First diagonal path legend
        itemY += 20;
        g2d.setColor(new Color(200, 0, 0));
        g2d.setStroke(new BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        g2d.drawLine(legendX + 12, itemY, legendX + 36, itemY);
        g2d.setColor(Color.BLACK);
        g2d.drawString("Diagonal Path 1", legendX + 46, itemY + 4);
        
        // Second diagonal path legend
        itemY += 20;
        g2d.setColor(new Color(0, 100, 200));
        g2d.setStroke(new BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        g2d.drawLine(legendX + 12, itemY, legendX + 36, itemY);
        g2d.setColor(Color.BLACK);
        g2d.drawString("Diagonal Path 2", legendX + 46, itemY + 4);
    }
    
    /**
     * 高亮所有可移动的棋子
     */
    private void highlightMovablePieces(Graphics2D g2d) {
        Player currentPlayer = game.getCurrentPlayer();
        if (canSelectPieces && game.hasRolled()) {
            g2d.setColor(new Color(200, 0, 0, 200));
            g2d.setFont(new Font("Arial", Font.BOLD, 14));
            String hintText = "Select one of " + currentPlayer.getName() + "'s pieces to move";
            FontMetrics fm = g2d.getFontMetrics();
            int textWidth = fm.stringWidth(hintText);
            g2d.drawString(hintText, (getWidth() - textWidth) / 2, 80); // 调整文本位置
            
            // 计算脉冲效果大小
            float pulseSize = 1.0f + (float)Math.sin(pulseCounter * 0.6) * 0.1f;
            
            // 查找所有可移动的棋子
            for (Piece piece : currentPlayer.getPieces()) {
                // 任何不是已完成的棋子都可以移动
                if (piece.getPosition() != 30 && !piece.isStacked()) {
                    Point pos = getPieceDrawPosition(piece);
                    if (pos != null) {
                        // Draw pulsing highlight around movable pieces
                        int highlightSize = (int)(PIECE_SIZE * 2 * pulseSize);
                        g2d.setColor(new Color(255, 215, 0, 150)); // Gold color
                        g2d.fillOval(pos.x - highlightSize/2, pos.y - highlightSize/2, 
                            highlightSize, highlightSize);
                        
                        // 添加"可移动"指示文本
                        g2d.setColor(new Color(0, 0, 0));
                        g2d.setFont(new Font("Arial", Font.BOLD, 10));
                        g2d.drawString("Click", pos.x - 10, pos.y - PIECE_SIZE - 5);
                    } else if (piece.getPosition() == -1) {
                        // Highlight home area for pieces not on board
                        int playerIndex = 0;
                        for (Player player : game.getPlayers()) {
                            if (player == currentPlayer) {
                                break;
                            }
                            playerIndex++;
                        }
                        
                        Rectangle homeArea = homeAreas[playerIndex];
                        g2d.setColor(new Color(255, 215, 0, 100)); // Gold color
                        
                        // 绘制带脉冲效果的高亮
                        int pulseWidth = (int)(homeArea.width * pulseSize);
                        int pulseHeight = (int)(homeArea.height * pulseSize);
                        int pulseX = homeArea.x - (pulseWidth - homeArea.width)/2;
                        int pulseY = homeArea.y - (pulseHeight - homeArea.height)/2;
                        
                        g2d.fillRect(pulseX, pulseY, pulseWidth, pulseHeight);
                        g2d.setColor(new Color(255, 215, 0));
                        g2d.setStroke(new BasicStroke(3.0f));
                        g2d.drawRect(homeArea.x, homeArea.y, homeArea.width, homeArea.height);
                        
                        // Add text hint for new pieces
                        g2d.setColor(new Color(0, 0, 0));
                        g2d.setFont(new Font("Arial", Font.BOLD, 12));
                        String homeHint = "Click to place a new piece";
                        g2d.drawString(homeHint, homeArea.x + 10, homeArea.y + homeArea.height / 2);
                    }
                }
            }
        }
    }
    
    /**
     * 绘制当前玩家回合指示器
     */
    private void drawTurnIndicator(Graphics2D g2d) {
        Player currentPlayer = game.getCurrentPlayer();
        if (currentPlayer == null) return;
        
        // 在右上角绘制回合指示器，避开提示标签
        int width = 140;
        int height = 40;
        int x = getWidth() - width - 10; // 右上角
        int y = 10;
        
        // 使用当前玩家颜色的背景
        g2d.setColor(currentPlayer.getColor());
        g2d.fillRoundRect(x, y, width, height, 10, 10);
        
        // 黑色边框
        g2d.setColor(Color.BLACK);
        g2d.setStroke(new BasicStroke(2f));
        g2d.drawRoundRect(x, y, width, height, 10, 10);
        
        // 白色文字显示玩家名称和"Turn"
        g2d.setColor(Color.WHITE);
        g2d.setFont(new Font("SansSerif", Font.BOLD, 14));
        g2d.drawString(currentPlayer.getName() + "'s Turn", x + 10, y + 25);
    }
    
    /**
     * 绘制玩家信息显示
     */
    private void drawPlayerInfo(Graphics2D g2d) {
        // 使用homeAreas自带的玩家信息显示，不需要额外绘制
        // 此方法保留，但不执行任何操作
    }
    
    /**
     * 更新当前游戏动作提示标签
     */
    private void updateCurrentActionLabel() {
        if (currentActionLabel != null) {
            String message;
            if (!game.hasRolled()) {
                message = "Please roll the Yut sticks first";
            } else if (!game.getAvailableRolls().isEmpty()) {
                message = "Select a piece to move";
            } else if (game.getCurrentRoll() == YutSet.YUT || game.getCurrentRoll() == YutSet.MO) {
                String rollName = game.getCurrentRoll() == YutSet.YUT ? "Yut" : "Mo";
                message = "You rolled " + rollName + "! Click to throw again!";
            } else {
                message = "Click 'End Turn' to finish your turn";
            }
            currentActionLabel.setText(message);
        }
    }

    /**
     * Handles the end of the current player's turn.
     * Updates UI state and transitions to the next player.
     */
    public void handleEndTurn() {
        System.out.println("\n=== Handle End Turn ===");
        System.out.println("Current player before endTurn: " + game.getCurrentPlayer().getName());
        
        // 检查是否是特殊点数（윷 Yut=4 或 모 Mo=5）
        int lastRoll = game.getCurrentRoll();
        boolean shouldGetExtraTurn = (lastRoll == YutSet.YUT || lastRoll == YutSet.MO);
        
        if (shouldGetExtraTurn) {
            System.out.println("Player gets extra turn due to Yut or Mo roll!");
            // 重置当前回合状态但不切换玩家
            game.setHasRolled(false);
            game.clearAvailableRolls();
            game.setCurrentRoll(0);
            
            // 重置UI状态
            selectedPieces.clear();
            possibleMovePositions.clear();
            showingHints = false;
            canSelectPieces = false;
            
            // 获取并更新骰子面板
            YutStickPanel yutPanel = getYutStickPanel();
            if (yutPanel != null) {
                yutPanel.reset();
                yutPanel.setEnabled(true);
                yutPanel.setThrowEnabled(true);
                yutPanel.updateMessage("You got an extra turn! Throw again!");
                System.out.println("YutStickPanel enabled for extra turn");
            }
        } else {
            // 正常结束回合，切换到下一个玩家
            System.out.println("Normal turn end - switching players");
            game.endTurn();
            System.out.println("Current player after endTurn: " + game.getCurrentPlayer().getName());
            
            // 重置所有游戏状态
            game.setHasRolled(false);
            game.clearAvailableRolls();
            game.setCurrentRoll(0);
            
            // 重置UI状态
            selectedPieces.clear();
            possibleMovePositions.clear();
            showingHints = false;
            canSelectPieces = false;
            
            // 更新UI并启用骰子面板
            YutStickPanel yutPanel = getYutStickPanel();
            yutPanel.reset();
            yutPanel.setEnabled(true);
            yutPanel.setThrowEnabled(true);
            yutPanel.updateMessage(game.getCurrentPlayer().getName() + "'s turn - Click to throw!");
            System.out.println("YutStickPanel enabled for new player");
            
            updatePlayerTurn();
        }
        
        // 更新当前玩家标签
        updateCurrentActionLabel();
        
        repaint();
    }

    private void updatePlayerTurn() {
        System.out.println("\n=== Update Player Turn ===");
        // Update current action label
        String playerName = game.getCurrentPlayer().getName();
        System.out.println("Updating turn for player: " + playerName);
        currentActionLabel.setText(playerName + " - Please roll the Yut sticks");
        
        // Reset and enable YutStickPanel
        YutStickPanel yutPanel = getYutStickPanel();
        yutPanel.reset();
        yutPanel.setEnabled(true);
        yutPanel.setThrowEnabled(true);
        yutPanel.updateMessage("Click to throw Yut sticks");
        System.out.println("YutStickPanel state: " + (yutPanel.isEnabled() ? "enabled" : "disabled"));
        
        repaint();
    }

    /**
     * 绘制移动提示
     */
    private void drawMovementHints(Graphics2D g2d) {
        if (selectedPieces.isEmpty()) {
            // 高亮所有可移动的棋子
            highlightMovablePieces(g2d);
        } else {
            // 高亮选中棋子的移动路径
            drawPossibleMoves(g2d);
        }
    }

    /**
     * Finishes the current move.
     * If there are no more available rolls or re-throws, the turn is considered complete.
     */
    public void finishMove() {
        System.out.println("\n=== Finish Move ===");
        System.out.println("Finishing move for " + game.getCurrentPlayer().getName() + 
                         "\nAvailable rolls: " + game.getAvailableRolls().size() + 
                         "\nRe-throw allowed: " + game.isReThrowAllowed());
        
        // Reset UI state
        selectedPieces.clear();
        possibleMovePositions.clear();
        showingHints = false;
        canSelectPieces = false;
        
        // If there are more available rolls, the player can still move pieces
        if (!game.getAvailableRolls().isEmpty()) {
            System.out.println("Player still has available rolls: " + game.getAvailableRolls());
            showStatusMessage(game.getCurrentPlayer().getName() + "님, 남은 윷을 사용해주세요"); // Use remaining rolls
            return;
        }
        
        // If re-throw is allowed, enable the Yut throw button
        if (game.isReThrowAllowed()) {
            game.setHasRolled(false);  // Allow player to throw again
            System.out.println("Player can throw again");
            showStatusMessage(game.getCurrentPlayer().getName() + "님, 한번 더 던지실 수 있습니다"); // You can throw again
            
            // Enable Yut throw button
            YutStickPanel yutPanel = getYutStickPanel();
            if (yutPanel != null) {
                System.out.println("YutStickPanel found for re-throw, enabling...");
                yutPanel.setEnabled(true);
                yutPanel.updateMessage("You can throw again!");
                System.out.println("YutStickPanel state: " + (yutPanel.isEnabled() ? "enabled" : "disabled"));
            } else {
                System.out.println("WARNING: YutStickPanel not found for re-throw!");
            }
            return;
        }
        
        // Otherwise, the turn is over
        System.out.println("Turn is over, switching to next player");
        game.endTurn();
        showStatusMessage(game.getCurrentPlayer().getName() + "님의 차례입니다"); // It's [player]'s turn
        
        // Enable Yut throw button for next player
        YutStickPanel yutPanel = getYutStickPanel();
        if (yutPanel != null) {
            System.out.println("YutStickPanel found for next player, enabling...");
            yutPanel.setEnabled(true);
            yutPanel.updateMessage("Click to throw Yut sticks");
            System.out.println("YutStickPanel state: " + (yutPanel.isEnabled() ? "enabled" : "disabled"));
        } else {
            System.out.println("WARNING: YutStickPanel not found for next player!");
        }
    }

    /**
     * Shows a status message to the user.
     * 
     * @param message The message to display
     */
    private void showStatusMessage(String message) {
        if (currentActionLabel != null) {
            currentActionLabel.setText(message);
            currentActionLabel.setForeground(new Color(50, 50, 50));
            currentActionLabel.setVisible(true);
        }
        System.out.println("Status message: " + message);
    }

    // 新增：检查玩家是否所有棋子都在家中
    private boolean allPiecesAtHome(Player player) {
        for (Piece piece : player.getPieces()) {
            if (piece.getPosition() != -1) {
                return false;
            }
        }
        return true;
    }

    /**
     * Handles piece movement based on the current roll.
     */
    private void handlePieceMovement(Piece piece) {
        int currentPosition = piece.getPosition();
        int previousPosition = selectedPiece != null ? selectedPiece.getPosition() : -1;

        // Check if we're at a junction point (5, 10, 15, or 23)
        if (board.isJunction(currentPosition)) {
            Map<Board.PathType, Integer> paths = board.getAvailablePaths(currentPosition, previousPosition);
            if (!paths.isEmpty()) {
                Board.PathType chosenPath = showPathSelectionDialog(paths);
                if (chosenPath != null) {
                    // Move piece with chosen path
                    game.movePiece(piece, game.getCurrentRoll(), chosenPath);
                }
            }
        } else {
            // Move piece normally
            game.movePiece(piece, game.getCurrentRoll());
        }
        
        // Update UI after move
        updateUIAfterMove();
    }

    private void handlePieceClick(Piece piece) {
        // Check if it's the current player's piece
        if (!game.isCurrentPlayer(piece.getOwner())) {
            showErrorHint("You can only move your own pieces!");
            return;
        }
        
        // If the piece is stacked, use the stack leader
        Piece targetPiece = piece.isStacked() ? piece.getStackLeader() : piece;
        
        // Toggle piece selection
        if (selectedPiece == targetPiece) {
            // Deselect if already selected
            selectedPiece = null;
            repaint();
            return;
        }
        
        // Select the piece
        selectedPiece = targetPiece;
        repaint();
        
        // Calculate and show possible moves
        if (game.hasRolled()) {
            List<Integer> availableRolls = game.getAvailableRolls();
            if (!availableRolls.isEmpty()) {
                int roll = availableRolls.get(0);
                if (game.canMove(targetPiece, roll)) {
                    // Show movement options
                    calculatePossibleMoves(roll);
                    showMovementHint(roll);
                } else {
                    selectedPiece = null;
                    showErrorHint("This piece cannot be moved with the current roll!");
                }
            }
        }
    }

    /**
     * Shows a dialog for selecting which path to take at a junction
     */
    private Board.PathType showPathSelectionDialog(Map<Board.PathType, Integer> paths) {
        if (paths.size() <= 1) {
            return paths.keySet().iterator().next();
        }

        // Create dialog with custom styling
        JDialog dialog = new JDialog((Frame)SwingUtilities.getWindowAncestor(this), "Path Selection", true);
        dialog.setLayout(new BorderLayout(10, 10));
        dialog.setBackground(new Color(245, 245, 245));
        
        // Add title label
        JLabel titleLabel = new JLabel("<html><center>Choose Your Path</center></html>", JLabel.CENTER);
        titleLabel.setFont(new Font("SansSerif", Font.BOLD, 16));
        titleLabel.setBorder(BorderFactory.createEmptyBorder(15, 10, 10, 10));
        dialog.add(titleLabel, BorderLayout.NORTH);
        
        // Create panel for buttons
        JPanel buttonPanel = new JPanel(new GridLayout(paths.size(), 1, 10, 10));
        buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 20, 20, 20));
        buttonPanel.setBackground(new Color(245, 245, 245));
        
        final Board.PathType[] selectedPath = new Board.PathType[1];
        
        for (Board.PathType pathType : paths.keySet()) {
            JButton button = new JButton();
            button.setPreferredSize(new Dimension(280, 80));
            
            // Set button style based on path type
            switch (pathType) {
                case MAIN_PATH:
                    button.setText("<html><center>Main Path<br><font size='2'>(11→12→13)</font></center></html>");
                    button.setBackground(new Color(139, 69, 19));  // Brown
                    break;
                case DIAGONAL_RIGHT:
                    button.setText("<html><center>Right Diagonal<br><font size='2'>(10→27→22)</font></center></html>");
                    button.setBackground(new Color(70, 130, 180));  // Blue
                    break;
                case DIAGONAL_LEFT:
                    button.setText("<html><center>Left Diagonal<br><font size='2'>(15→29→21)</font></center></html>");
                    button.setBackground(new Color(178, 34, 34));  // Red
                    break;
            }
            
            button.setForeground(Color.WHITE);
            button.setFont(new Font("SansSerif", Font.BOLD, 14));
            button.setFocusPainted(false);
            button.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(button.getBackground().darker(), 2),
                BorderFactory.createEmptyBorder(8, 12, 8, 12)
            ));
            
            // Add hover effect
            button.addMouseListener(new MouseAdapter() {
                public void mouseEntered(MouseEvent e) {
                    button.setBackground(button.getBackground().brighter());
                }
                
                public void mouseExited(MouseEvent e) {
                    button.setBackground(button.getBackground().darker());
                }
            });
            
            // Add action listener
            final Board.PathType currentPath = pathType;
            button.addActionListener(e -> {
                selectedPath[0] = currentPath;
                dialog.dispose();
            });
            
            buttonPanel.add(button);
        }
        
        dialog.add(buttonPanel, BorderLayout.CENTER);
        dialog.pack();
        dialog.setLocationRelativeTo(this);
        
        // Show dialog and wait for selection
        dialog.setVisible(true);
        
        // Return selected path or default to main path
        return selectedPath[0] != null ? selectedPath[0] : Board.PathType.MAIN_PATH;
    }
} 